﻿using Core.DataAccess.Model;
using Core.DataAccess.Model.Project.Queue;
using Core.Framework.Redis.Queue_Helper;
using Core.Framework.Util;
using Core.Framework.Util.Attributes;
using Core.IBusiness.IUserModule;
using System;
using System.Linq;
using System.Linq.Expressions;
using Core.Framework.Model.WebSockets;
using Newtonsoft.Json;
using Core.DataAccess.Model.Projects;

namespace Core.Business.UserModule
{
    public class SocketUsers : ISocketUser
    {
        public SocketUsers()
        {
        }

        public Tuple<bool, ProjectModuleUser> GetUserInfoByToken(string token, string projectToken)
        {
            var userInfo
                = RedisQueueHelper
                    .HashGet(RedisQueueHelperParameter.ProjectSocketUserLogin, projectToken + token);

            var model = ((string) userInfo).TryToEntity<ProjectModuleUser>();

            if (model != null)
            {
                if (model.EndTime > DateTime.Now)
                    return new Tuple<bool, ProjectModuleUser>(true, model);
                else
                    return new Tuple<bool, ProjectModuleUser>(false, model);
            }

            return new Tuple<bool, ProjectModuleUser>(false, null);
        }

        public Tuple<ProjectModuleUser, bool> Login(string user, string password, string projectToken, int hour)
        {
            using (var context = new ProjectSocketContext())
            {

                var userInfo =
                    context
                        .ProjectModuleUser
                        .Where(a => a.UserId == user && a.Pass == Md5Helper.Hash(password) &&
                                    a.ProjectToken == projectToken)
                        .FirstOrDefault();

                return this.UserInfoExec(context, userInfo, projectToken, hour);

            }

            ;
        }

        public Tuple<ProjectModuleUser, bool> LoginByClient(string uuid, string projectToken, int hour)
        {
            using (var context = new ProjectSocketContext())
            {
                var userInfo =
                    context
                        .ProjectModuleUser
                        .Where(a => a.Uuid == uuid && a.ProjectToken == projectToken)
                        .FirstOrDefault();

                if (!this.UserInfoExec(context, userInfo, projectToken, hour).Item2)
                {
                    userInfo = new ProjectModuleUser
                    {
                        Uuid = uuid,
                        ProjectToken = projectToken
                    };

                    var tuple = this.Reg(userInfo);

                    if (tuple.Item1)
                        return this.UserInfoExec(context, tuple.Item2, projectToken, hour);
                    else
                        return new Tuple<ProjectModuleUser, bool>(new ProjectModuleUser{ Pass = tuple .Item3}, false);

                }


                return this.UserInfoExec(context, userInfo, projectToken, hour);
            }

            ;
        }

        private Tuple<ProjectModuleUser, bool> UserInfoExec(ProjectSocketContext context, ProjectModuleUser userInfo,
            string projectToken, int hour)
        {
            if (null != userInfo && userInfo.Id > 0)
            {
                // 同一用户 只保存一个token
                this.OutLogin(userInfo.Token, projectToken);

                // 用户临时token
                userInfo.Token = Md5Helper.Hash(Guid.NewGuid().ToString());

                // 过期时间
                userInfo.EndTime = DateTime.Now.AddHours(hour);

                // 更新用户token
                context.SaveChanges();

                // 写入缓存
                RedisQueueHelper
                    .HashSet(
                        RedisQueueHelperParameter.ProjectSocketUserLogin,
                        projectToken + userInfo.Token, userInfo.TryToJson());

                return
                    new Tuple<ProjectModuleUser, bool>(userInfo, true);
            }

            return new Tuple<ProjectModuleUser, bool>(null, false);
        }

        public Tuple<ProjectModuleUser, bool> LoginByPhone(string phone, string password, string projectToken, int hour)
        {
            using (var context = new ProjectSocketContext())
            {

                var userInfo =
                    context
                        .ProjectModuleUser
                        .Where(a => a.Phone == phone && a.Pass == Md5Helper.Hash(password) &&
                                    a.ProjectToken == projectToken)
                        .FirstOrDefault();

                return this.UserInfoExec(context, userInfo, projectToken, hour);

            }

            ;
        }

        public Tuple<ProjectModuleUser, bool> LoginByWChat(string openID, string projectToken, int hour)
        {
            using (var context = new ProjectSocketContext())
            {
                var userInfo =
                    context
                        .ProjectModuleUser
                        .Where(a => a.WxOpenid == openID && a.ProjectToken == projectToken)
                        .FirstOrDefault();

                if (!this.UserInfoExec(context, userInfo, projectToken, hour).Item2)
                {
                    userInfo = new ProjectModuleUser
                    {
                        WxOpenid = openID,
                        ProjectToken = projectToken
                    };

                    var tuple = this.Reg(userInfo);

                    if (tuple.Item1)
                        return this.UserInfoExec(context, tuple.Item2, projectToken, hour);

                }

                return this.UserInfoExec(context, userInfo, projectToken, hour);
            }

            ;
        }

        public void OutLogin(string token, string projectToken)
        {
            RedisQueueHelper.HashDelete(RedisQueueHelperParameter.ProjectSocketUserLogin, projectToken + token);
            RedisQueueHelper.HashDelete(RedisQueueHelperParameter.WebSocketByToken, token);
        }

        public Tuple<bool, ProjectModuleUser, string> Reg(ProjectModuleUser model)
        {
            if (model == null)
                return new Tuple<bool, ProjectModuleUser, string>(false, model, "数据为NULL");

            var result = model.IsValid(Attributes.IsValidEnum.reg);

            if (!result.Item1)
                return new Tuple<bool, ProjectModuleUser, string>(result.Item1, model, result.Item2);

            if (!string.IsNullOrWhiteSpace(model.Pass))
                model.Pass = Md5Helper.Hash(model.Pass);

            if (string.IsNullOrWhiteSpace(model.Uuid)
                && string.IsNullOrWhiteSpace(model.Phone)
                && string.IsNullOrWhiteSpace(model.WxOpenid)
                && string.IsNullOrWhiteSpace(model.BaiduOpenid)
            )
            {
                return new Tuple<bool, ProjectModuleUser, string>(false, model, "请指定注册账户");
            }

            
            using (var context = new ProjectContext())
            {
                var key = context.ProjectList.Where(a => a.Token == model.ProjectToken).Select(a => a.Id).FirstOrDefault();
                if (key < 1)
                    return new Tuple<bool, ProjectModuleUser, string>(false, model, "该项目不存在");
            }

            using (var context = new ProjectSocketContext())
            {
                var userInfo =
                    context.ProjectModuleUser
                        .Where(a =>
                            (a.Phone == model.Phone && a.ProjectToken == model.ProjectToken &&
                             !string.IsNullOrWhiteSpace(model.Phone))
                            || (a.Uuid == model.Uuid && a.ProjectToken == model.ProjectToken &&
                                !string.IsNullOrWhiteSpace(model.Uuid))
                            || (a.WxOpenid == model.WxOpenid && a.ProjectToken == model.ProjectToken &&
                                !string.IsNullOrWhiteSpace(model.WxOpenid))
                            || (a.UserId == model.UserId && a.ProjectToken == model.ProjectToken &&
                                !string.IsNullOrWhiteSpace(model.UserId))
                            || (a.BaiduOpenid == model.BaiduOpenid && a.ProjectToken == model.ProjectToken &&
                                !string.IsNullOrWhiteSpace(model.BaiduOpenid))
                        ).FirstOrDefault();

                if (userInfo?.Id > 0)
                    return new Tuple<bool, ProjectModuleUser, string>(false, model, "该账号已注册");

                if (!string.IsNullOrWhiteSpace(model.Pass))
                    model.Pass = Md5Helper.Hash(model.Pass);

                model.AddTime = DateTime.Now;
                model.Token = Md5Helper.Hash($"{model.Phone}_z_%^&*");
                context.ProjectModuleUser.Add(model);
                context.SaveChanges();
                return new Tuple<bool, ProjectModuleUser, string>(true, model, "注册成功");
                ;
            }

            ;
        }

        public ProjectModuleUser UpdateUserInfo(ProjectModuleUser model)
        {
            using (var context = new ProjectSocketContext())
            {
                Expression<Func<ProjectModuleUser, bool>>

                    login
                        = a =>
                            a.Token == model.Token

                            && (
                                (a.Phone == model.Phone
                                 && a.ProjectToken == model.ProjectToken
                                 && a.Pass == Md5Helper.Hash(model.Pass)
                                 && !string.IsNullOrWhiteSpace(model.Phone))

                                || (a.Uuid == model.Uuid
                                    && a.ProjectToken == model.ProjectToken
                                    && !string.IsNullOrWhiteSpace(model.Uuid))

                                || (a.WxOpenid == model.WxOpenid
                                    && a.ProjectToken == model.ProjectToken
                                    && !string.IsNullOrWhiteSpace(model.WxOpenid))

                                || (a.UserId == model.UserId
                                    && a.ProjectToken == model.ProjectToken
                                    && a.Pass == Md5Helper.Hash(model.Pass)
                                    && !string.IsNullOrWhiteSpace(model.UserId))

                                || (a.BaiduOpenid == model.BaiduOpenid
                                    && a.ProjectToken == model.ProjectToken
                                    && !string.IsNullOrWhiteSpace(model.BaiduOpenid))
                            );

                var userInfo = context.ProjectModuleUser.Where(login).FirstOrDefault();

                if (userInfo != null)
                {
                    userInfo.Combine(model, "Id", "id", "AddTime");
                    context.SaveChanges();
                    return model;
                }

                return null;

            }

            ;
        }

        public Tuple<ProjectModuleUser, bool> LoginByKey(int userID, string projectToken, int hour)
        {
            using (var context = new ProjectSocketContext())
            {
                var userInfo =
                    context
                        .ProjectModuleUser
                        .Where(a => a.Id == userID && a.ProjectToken == projectToken)
                        .FirstOrDefault();
                return this.UserInfoExec(context, userInfo, projectToken, hour);
            }

            ;
        }

        public ProjectModuleUser UpdateUserInfoParas(int key, string userToken, string projectToken, string paras)
        {
            using (var context = new ProjectSocketContext())
            {
                var userInfo = context.ProjectModuleUser
                    .Where(a =>
                        a.Id == key && a.Token == userToken && a.ProjectToken == projectToken
                    )
                    .FirstOrDefault();

                if (userInfo != null && userInfo.Id > 0)
                {

                    userInfo.Paras = userInfo.Paras.TryCombineJObject(paras).TryToJson();
                    context.SaveChanges();
                    this.UpdateUserParas(userInfo.Token, userInfo.ProjectToken, userInfo.Paras);
                    return userInfo;
                }

                return null;

            }

            ;
        }

        public Tuple<ProjectModuleUser, string, bool> LoginWebSocketByUserToken(string userToken, string projectToken,
            string parameter, string url)
        {
            var tuple = this.GetUserInfoByToken(userToken, projectToken);

            if (tuple.Item1)
                return this.WebSocketExec(tuple.Item2, parameter, url);

            return new Tuple<ProjectModuleUser, string, bool>(tuple.Item2, tuple.Item2 == null ? "信息不存在" : "登陆过期",
                tuple.Item1);
        }

        public Tuple<ProjectModuleUser, string, bool> LoginWebSocketByClient(string uuid, string projectToken,
            string parameter, string url)
        {
            var tuple = this.LoginByClient(uuid, projectToken, 12);

            if (tuple.Item2)
            {
                return this.WebSocketExec(tuple.Item1, parameter, url);
            }

            return new Tuple<ProjectModuleUser, string, bool>(null, tuple?.Item1?.Pass, tuple.Item2);
        }

        public Tuple<ProjectModuleUser, string, bool> LoginWebSocketByWChat(string openID, string projectToken,
            string parameter, string url)
        {
            var tuple = this.LoginByWChat(openID, projectToken, 12);

            if (tuple.Item2)
                return this.WebSocketExec(tuple.Item1, parameter, url);

            return new Tuple<ProjectModuleUser, string, bool>(tuple.Item1, "系统异常请刷新重试", tuple.Item2);
        }

        private Tuple<ProjectModuleUser, string, bool> WebSocketExec(ProjectModuleUser model, string parameter,
            string url)
        {

            string[] Subscription;

            // 接受分组消息
            using (var context = new ProjectSocketContext())
            {
                var groups = context.ProjectModuleGroupUser
                    .Where(a => a.UserId == model.Id.ToString() && a.ProjectToken == model.ProjectToken)
                    .GroupBy(a => a.GroupKey)
                    .Select(a => a.Key).ToList();

                // 接受数据
                Subscription = new string[groups.Count()+1];

                Subscription[0] = model.Id.ToString();

                int i = 1;
                foreach (var item in groups)
                {
                    Subscription[i] = $"group_{item}";
                    i++;
                }

            };

            // 删除用户登陆信息
            RedisQueueHelper.HashDelete(RedisQueueHelperParameter.ProjectSocketUserLogin,
                model.ProjectToken + model.Token);

            // 构造链接数据
            ClientInfo client = new ClientInfo
            {
                Project = new ProjectInfo
                {
                    ProjectToken = model.ProjectToken,
                    CallUrl = url
                },
                User = new UserInfo
                {
                    Key = model.Id.ToString(),
                    Parameter = model.Paras.TryCombineJObject(parameter).TryToJson(),
                    Subscription = Subscription
                }
            };

            // 生成令牌
            model.Token = Md5Helper.Hash(Guid.NewGuid().ToString());

            // 更新令牌
            using (var context = new ProjectSocketContext())
            {
                context.ProjectModuleUser.Update(model);
                context.SaveChanges();
            };

            // 写入缓存
            RedisQueueHelper.HashSet(RedisQueueHelperParameter.WebSocketByToken, model.Token, client.TryToJson());

            return new Tuple<ProjectModuleUser, string, bool>(model, model.Token, true);

        }

        #region Common

        /// <summary>
        /// 修改 Paras后更新缓存
        /// </summary>
        /// <param name="usertoken"></param>
        /// <param name="projecttoken"></param>
        /// <param name="paras"></param>
        private void UpdateUserParas(string usertoken, string projecttoken,string paras)
        {
            // 读出WS缓存
            var wsValue = (string)RedisQueueHelper.HashGet(RedisQueueHelperParameter.WebSocketByToken, usertoken);

            // 读出用户缓存
            var ueValue = (string)RedisQueueHelper.HashGet(RedisQueueHelperParameter.ProjectSocketUserLogin,projecttoken + usertoken);

            // 更新 WS信息
            if (!string.IsNullOrWhiteSpace(wsValue))
            {
                ClientInfo client = wsValue.TryToEntity<ClientInfo>();
                client.User.Parameter = client.User.Parameter.TryCombineJObject(paras).TryToJson();


                var clients = WebSocketApplication.ClientsPool.Where(a => a.Value.ClientToken == client.ClientToken);

                if (clients?.Count() > 0)
                {
                    foreach (var item in clients)
                        WebSocketApplication.ClientsPool[item.Key] = client;
                }

                RedisQueueHelper.HashSet(RedisQueueHelperParameter.WebSocketByToken, usertoken, client.TryToJson());
            }

            // 更新 用户登陆信息
            if (!string.IsNullOrWhiteSpace(ueValue))
            {
                ProjectModuleUser userInfo = ueValue.TryToEntity<ProjectModuleUser>();
                userInfo.Paras = userInfo.Paras.TryCombineJObject(paras).TryToJson();
                RedisQueueHelper.HashSet(RedisQueueHelperParameter.ProjectSocketUserLogin, projecttoken + usertoken, userInfo.TryToJson()); 
            }

        }

        #endregion

    }
}
